package com.wlf.server.web;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil;
import com.fasterxml.jackson.core.type.TypeReference;
import com.wlf.server.common.entity.SysApiLog;
import com.wlf.server.common.service.SysApiLogService;
import com.wlf.server.web.util.AjaxBean;
import com.wlf.server.web.util.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.Ordered;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.util.ContentCachingRequestWrapper;
import org.springframework.web.util.ContentCachingResponseWrapper;

import javax.annotation.Resource;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.Map;

/**
 * 主要是用来记录http日志，如果每次请求都写库的话这个有点损耗性能阿。
 * 明显感觉响应速度变慢
 * 可以在这里做接口限流。
 */
@Slf4j
@Service
public class ApiLogFilter extends OncePerRequestFilter implements Ordered {

    @Resource
    private SysApiLogService apiLogService;

    /**
     * 可以使用这个指定哪些请求，经过此过滤器，也就是记录请求记录，
     * 实际中所有请求记录皆需要记录的需求应该不多，
     * 记录的话如果请求一次写一次数据库，只能说优化空间很大。
     *
     * @param request current HTTP request
     * @return true 不进入此过滤器，false 进入此过滤器
     */
    @Override
    protected boolean shouldNotFilter(@NonNull HttpServletRequest request) {

        if (HttpMethod.OPTIONS.name().equals(request.getMethod())) {
            return true;
        }
        // 这个类可以用来匹配URl。
        //AntPathMatcher pathMatcher = new AntPathMatcher();

        log.debug(request.getRequestURI());
        return false;
    }

    @Override
    protected void doFilterInternal(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response, @NonNull FilterChain filterChain) throws ServletException, IOException {

        SysApiLog apiLog = new SysApiLog();
        apiLog.setRequestTime(LocalDateTime.now());
        ContentCachingRequestWrapper requestWrapper = new ContentCachingRequestWrapper(request);

        ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper(response);

        try {
            // 这里就是执行了请求
            filterChain.doFilter(requestWrapper, responseWrapper);
            String requestURI = requestWrapper.getRequestURI();
            String method = requestWrapper.getMethod();
            String contentType = requestWrapper.getContentType();
            String loginUserId = "";
            try {
                loginUserId = StpUtil.getLoginIdAsString();
            } catch (Exception ignored) {
                log.info("未登录访问请求");
            }
            apiLog.setUserId(loginUserId);
            apiLog.setMethod(method);
            apiLog.setUrl(requestURI);

            log.debug("request=url:{},method:{},contentType:{}",
                    requestURI,
                    method,
                    contentType
            );
            if (HttpMethod.GET.name().equals(method)) {
                String queryString = requestWrapper.getQueryString();
                log.debug("queryString:{}", queryString);
                apiLog.setQueryString(queryString);
            }
            if (HttpMethod.POST.name().equals(method)) {
                if (StringUtils.hasLength(contentType)) {
                    if (contentType.contains(MediaType.APPLICATION_FORM_URLENCODED_VALUE)) {
                        String formData = new String(requestWrapper.getContentAsByteArray(),
                                requestWrapper.getCharacterEncoding());
                        formData = URLUtil.decode(formData);
                        log.debug("formData:{}", formData);
                        apiLog.setFormData(formData);
                    }
                    if (contentType.contains(MediaType.APPLICATION_JSON_VALUE)) {
                        String jsonData = new String(requestWrapper.getContentAsByteArray(),
                                requestWrapper.getCharacterEncoding());
                        log.debug("jsonData:{}", jsonData);
                        apiLog.setJsonData(jsonData);
                    }
                }
            }
            String respContentType = responseWrapper.getContentType();
            int status = responseWrapper.getStatus();
            if (respContentType != null) {
                if (respContentType.contains(MediaType.APPLICATION_JSON_VALUE)) {
                    AjaxBean resultBean = AjaxBean.getOkMsg()
                            .setMap(JSON.parseObject(
                                    new String(responseWrapper.getContentAsByteArray(),
                                            StandardCharsets.UTF_8),
                                    new TypeReference<Map<String, Object>>() {
                                    }
                            ));
                    log.debug("resultCode:{}", resultBean.getCode());
                    apiLog.setResponseCode(StrUtil.format(
                            "HttpCode:{},ResultCode:{}",
                            status,
                            String.valueOf(resultBean.getCode())
                    ));
                }
            }
            // 保存
            apiLogService.save(apiLog);
        } finally {
            responseWrapper.copyBodyToResponse();
        }

    }

    @Override
    public int getOrder() {
        return -100;
    }
}


